---
title: UnlockAccount
summary: Unlocks a locked-out external-domain account, gated by the IP allow-list.
status: draft
owner: "Not determined from source"
last_reviewed: 2026-09-22
---

# UnlockAccount (OP-03)

## Request and response

| Part | Type | Description |
| --- | --- | --- |
| `inUserId` | `String` | The `SamAccountName` of the account to unlock. |

| Part | Type | Description |
| --- | --- | --- |
| `<return>` | `ADServiceResponse` | `Response` is `Success` on every completed lookup outcome (locked, not locked, or unlock attempted), `UserDoesNotExist` when no principal resolves, or `Failure` on a caught exception. `Message` carries a free-text distinction between these outcomes. |

## WebMethod attributes

`<WebMethod()>` — bare, no arguments supplied.

## Steps

1. `CheckAccess()`; on denial return `AccessDenied()` ([BR-01](../business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller)).
2. Open `Try`.
3. `Using userPrin = ` H2 `GetUserPrincipal` for the external domain.
4. If `Nothing`, return `Response = UserDoesNotExist` with a message naming the user id ([BR-14](../business-rules.md#br-14-an-unlock-request-for-an-unknown-user-is-reported-as-such)).
5. If `userPrin.IsAccountLockedOut()` is `True`:
   - Call `userPrin.UnlockAccount()`.
   - Re-test `IsAccountLockedOut()` and set `Message` to "the account was unlocked" or "the account was not unlocked" accordingly.
   - Return `Response = Success` in either case ([BR-15](../business-rules.md#br-15-an-account-is-unlocked-only-when-locked-and-the-result-is-re-verified)).
6. Else return `Response = Success` with the "account was not locked" wording.
7. `Catch ex As Exception` → return `Response = Failure`, `Message = ex.Message`.

## Process flow

```mermaid
flowchart TD
    A["Call UnlockAccount"] --> B{"CheckAccess<br>HasAccess?"}
    B -->|"No"| C["Return AccessDenied"]
    B -->|"Yes"| D["GetUserPrincipal<br>external domain"]
    D -->|"Nothing"| E["Return UserDoesNotExist"]
    D -->|"Found"| F{"IsAccountLockedOut?"}
    F -->|"Yes"| G["UnlockAccount<br>re-test lockout"]
    G --> H["Return Success<br>message reflects re-test"]
    F -->|"No"| I["Return Success<br>was not locked"]
    D -.->|"Exception"| J["Return Failure<br>ex.Message"]
```

## Shared state effects

None — this operation touches no process-wide state; all locals are per-request.

## Error handling

The operation always returns a sentinel `ADServiceResponse`; it never surfaces a framework SOAP fault*. Note it reports `Response = Success` even when the unlock did not take effect — the distinction is carried only in the free-text `Message`, not in `Response`. See [Faults](../faults.md) for the service-wide fault model.

## Authorization

Gated by [BR-01](../business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller): the caller must pass the IP allow-list check enforced by `CheckAccess`. See [Authentication and authorization](../authentication-authorization.md).

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Related documentation
- [Service index](../index.md)
- [Overview](../overview.md)
- [Architecture](../architecture.md)
- [Activation and hosting](../activation-hosting.md)
- [Operations](../operations.md)
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Authentication and authorization](../authentication-authorization.md)
- [Configuration](../configuration.md)
- [Dependencies](../dependencies.md)
- [Faults](../faults.md)
- [Operational runbook](../operational-runbook.md)
- [Tech debt](../tech-debt.md)

## Sources
- `src\ADUserServices\clsADUserServices.asmx.vb` (lines 200-226)
